﻿using DryIoc;
using HandyControl.Controls;
using IntoTheVoid.Models;
using IntoTheVoid.Units;
using IntoTheVoid.Units.Download;
using IntoTheVoid.ViewModels;
using IntoTheVoid.Views;
using Microsoft.VisualBasic.FileIO;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Security.Cryptography;
using System.Security.Policy;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Shapes;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TaskbarClock;
using DownloadProgressChangedEventArgs = IntoTheVoid.Units.DownloadProgressChangedEventArgs;
namespace IntoTheVoid.State
{

	public class DownloadState : State
	{
		// 	public DownLoadManager downloader { get; set; }
		public DownloadInfo downloaderInfo { get; set; }
		public DownloadController downloadController;
		private GameDownLoadMessage gameDownLoadMessage;
		private string downLoadTempFilePath;
		private double downloadPercent = 0.0f;
		private bool isPredownload = false;
		private string downloadFilePath;
		private string currentLink;
		public DownloadState(StateContext context, bool isPre = false)
		{
			isPredownload = isPre;
			buttonContent = "下载游戏";
			buttonEnabla = true;
			Context = context;
			Context.OnButtonStateArgs(new ButtonStateChangeEventArgs(buttonContent, buttonEnabla, false, true, "StartDownloadButton"));
			gameDownLoadMessage = new GameDownLoadMessage();
			errorMsg = null;
		}

		public override void Start(StateContext context)
		{
			if (Context.StateType == StateType.Downloading)
			{
				// 下载中，切换暂停
				buttonContent = "继续下载";
				buttonEnabla = true;
				Context.OnButtonStateArgs(new ButtonStateChangeEventArgs(buttonContent, buttonEnabla, true, false, "StartDownloadButton"));
				Pause(context);
				return;
			}

			if (downloadPercent == 100.0f)
				downloadPercent = 0.0f;

			// 点击下载
			var defaultPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Game");
			var path = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);
			var version = BaseInfoContentModel.Instance.GetValue<string>("version", "");

			var httpDownLoadInfo = GameDownloadConfigModel.Instance.DownLoadInfo;
			var latest = !isPredownload ? httpDownLoadInfo.OpenedLatest : httpDownLoadInfo.UnopenedLink;
			if (currentLink == null)
				currentLink = GetDownLoadLink(httpDownLoadInfo, !isPredownload);


			if (latest == "" || currentLink == "")
			{
				LogHelper.Error("httpDownLoadInfo == null;" + httpDownLoadInfo.ToString());
				 if (isPredownload)
					Context.OnTipsStateArgs(new TipsEventArgs("无预下载链接"));
				else if(httpDownLoadInfo.OpenedLatest == version && version != "")
				{
					Context.OnTipsStateArgs(new TipsEventArgs("检测到下载错误，请去设置点击【清理系统文件重新下载】后重新下载游戏，若依旧失败建议重启启动器或重启电脑再次尝试"));
				}
				else
					Context.OnTipsStateArgs(new TipsEventArgs("系统维护中，请稍后再试"));
			
				return;
			}


			string fileName = Common.GetFileNameFromUrl(currentLink);
			bool fileExists = Common.CheckFileExists(path, fileName);


			buttonContent = "暂停下载";
			buttonEnabla = true;
			Context.OnButtonStateArgs(new ButtonStateChangeEventArgs(buttonContent, buttonEnabla, true, false, "StartDownloadButtonPause"));

			if (fileExists && !isPredownload)
			{
				// 切换安装状态
				string filePath = System.IO.Path.Combine(path, fileName);
				Context.CurState = new InstallState(filePath, Context);
			}
			else
			{
				do
				{
					if (Context.StateType == StateType.DownloadingStop)
					{
						// 暂停恢复不删除文件
						break;
					}
					if (!currentLink.Contains("base"))
					{
						// 不是整包不删除
						break;
					}

					var tempFileName = System.IO.Path.GetFileName(currentLink);
					tempFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(tempFileName)).ToUpper();
					tempFileName = tempFileName.Length > 32 ? tempFileName.Substring(0, 32) : tempFileName;
					var filePath = System.IO.Path.Combine(path, tempFileName + "0.temp");
					if (File.Exists(filePath))
					{
						// 存在当前下载的文件，不删除
						break;
					}
					try
					{
						// 整包下载删除所有旧包
						if (Directory.Exists(path))
						{
							Directory.Delete(path, true);
						}
					}
					catch (Exception ex)
					{
						LogHelper.Error("Directory.Delete Error Directory:" + path + " error:" + ex);
					}

				} while (false);

				// 下载
				Context.StateType = StateType.Downloading;

				// old
				// string tempFileName = Common.GetTempFileName(fileName);
				// downLoadTempFilePath = System.IO.Path.Combine(path, tempFileName);
				// DownLoadResource(link, downLoadTempFilePath);

				// new
				downloadFilePath = System.IO.Path.Combine(path, fileName);


				if (!Directory.Exists(path))
				{
					Directory.CreateDirectory(path);
				}

				DownLoadResource(currentLink, path);
			}
		}

		private string GetDownLoadLink(DownLoadInfo info, bool isOpened = true)
		{
			string defaultLink = isOpened ? info.OpenedLink : info.UnopenedLink;
			string path = isOpened ? info.OpenedPath : info.UnopenedPath;

			// 前置条件检查
			if (info.DownloadHosts == null ||
				(isOpened && string.IsNullOrEmpty(info.OpenedPath)) ||
				(!isOpened && string.IsNullOrEmpty(info.UnopenedPath)))
			{
				return defaultLink;
			}
			
			// 先检查默认链接是否可以ping通
			var defaultHost = GetHostFromUrl(defaultLink);
			if (defaultHost != null)
			{
				var pingResult = MeasurePing(defaultHost);
				if (pingResult < 500) // 如果默认链接ping延迟小于500ms，直接使用
				{
					// LogHelper.Info("使用默认下载链接: " + defaultLink);
					return defaultLink;
				}
			}

			var info1 = "节点选择中，请稍等...";
			Context.OnProcessStateArgs(new ProcessStateChangeEventArgs(0, info1, "", ""));

			// 并行测速逻辑
			var speedTests = new ConcurrentDictionary<string, long>();
			Parallel.ForEach(info.DownloadHosts, url =>
			{
				try
				{
					var host = GetHostFromUrl(url);
					if (host == null) return;

					// 综合测速（Ping + HTTP）
					var pingTime = MeasurePing(host);
					var httpTime = MeasureHttp(url);

					if (pingTime < 500 && httpTime < 5000) // 有效阈值
					{
						var totalTime = pingTime + httpTime;
						speedTests.TryAdd(url, totalTime);
					}
					else
					{
						LogHelper.Error("Host Check Error :" + host);
					}
				}
				catch(Exception e) {
					LogHelper.Error("Speed Check Error :" + e.Message);
				}
			});

			// 选择最优链接
			var fastest = speedTests.OrderBy(kv => kv.Value)
								   .FirstOrDefault();

			return fastest.Key != null ? $"{fastest.Key}{path}" : defaultLink;
		}

		// 新版Ping测速（返回延迟毫秒数）
		private long MeasurePing(string host)
		{
			try
			{
				using (var ping = new Ping())
				{
					var reply = ping.Send(host, 2000); // 2秒超时
					return reply.Status == IPStatus.Success
						   ? reply.RoundtripTime
						   : long.MaxValue;
				}
			}
			catch { return long.MaxValue; }
		}

		//private long MeasureHttp(string url)
		//{
		//	var sw = Stopwatch.StartNew();
		//	try
		//	{
		//		var request = (HttpWebRequest)WebRequest.Create(url);
		//		request.Method = "HEAD";
		//		request.Timeout = 10000; // 3秒超时

		//		using (var response = (HttpWebResponse)request.GetResponse())
		//		{
		//			return response.StatusCode >= HttpStatusCode.OK &&
		//				   response.StatusCode < HttpStatusCode.BadRequest
		//				   ? sw.ElapsedMilliseconds
		//				   : long.MaxValue;
		//		}
		//	}
		//	catch (Exception e){
		//		LogHelper.Error($"MeasureHttp: {e.Message}");
		//		return long.MaxValue; 
		//	}
		//	finally { sw.Stop(); }
		//}
		private static readonly ConcurrentDictionary<string, IPAddress> DnsCache =
			new ConcurrentDictionary<string, IPAddress>();

		public long MeasureHttp(string url)
		{
			var sw = Stopwatch.StartNew();
			try
			{
				var request = (HttpWebRequest)WebRequest.Create(url);
				request.Method = "HEAD";
				request.Timeout = 10000;
				request.ProtocolVersion = HttpVersion.Version11;
				request.KeepAlive = true;
				request.UserAgent = "Mozilla/5.0 (compatible; SpeedTest/1.0)";
				request.AutomaticDecompression = DecompressionMethods.GZip;

				// 使用预解析的IP
				if (DnsCache.TryGetValue(new Uri(url).Host, out var ip))
				{
					request.ServicePoint.BindIPEndPointDelegate = (servicePoint, remoteEndPoint, retryCount) =>
						new IPEndPoint(ip, 443);
				}

				using (var response = (HttpWebResponse)request.GetResponse())
				{
					if (response.StatusCode >= HttpStatusCode.OK &&
						response.StatusCode < HttpStatusCode.BadRequest)
					{
						return sw.ElapsedMilliseconds;
					}
					else
					{
						return long.MaxValue;
					}
				}
			}
			catch { return long.MaxValue; }
			finally { sw.Stop(); }
		}
		public static string GetHostFromUrl(string url)
		{
			try
			{
				Uri uri = new Uri(url);
				return uri.Host;
			}
			catch (UriFormatException)
			{
				Console.WriteLine("无效的URL格式");
				return null;
			}
		}
		private bool PingHost(string host)
		{
			// host没解析出来就不ping了
			if (host == null)
				return true;
			try
			{
				using (Ping ping = new Ping())
				{
					PingReply reply = ping.Send(host, 5000);
					return reply.Status == IPStatus.Success;
				}
			}
			catch
			{
				return false;
			}
		}
		public static bool CheckUrlAccess(string url)
		{
			try
			{
				var request = (HttpWebRequest)WebRequest.Create(url);
				request.Method = "HEAD";
				request.Timeout = 5000;

				using (var response = (HttpWebResponse)request.GetResponse())
				{
					// 2xx和3xx状态码视为成功
					return (int)response.StatusCode >= 200 &&
						   (int)response.StatusCode < 400;
				}
			}
			catch (WebException ex)
			{
				LogHelper.Error("链接失败：" + ex);
				return false;
			}
			catch
			{
				return false;
			}
		}
		private bool CheckDiskIsEnough(long total)
		{
			bool result = true;
			var defaultPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Game");
			var path = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);
			string disk = path.Substring(0, path.IndexOf(":"));
			long space = Common.GetHardDiskFreeSpace(disk);
			if (total * 2 > space)
			{
				result = false;
			}
			return result;
		}
		public override void Done(StateContext context)
		{
			if (Context.StateType == StateType.DownloadingStop)
			{
				return;
			}
			if (errorMsg == null)
			{
				// var newFilPath = Common.ChangeFileName(downLoadTempFilePath);
				// 这里应该检查md5
				//if (CheckFileComplete(gameDownLoadMessage.Total, newFilPath))
				//{

				if (!isPredownload)
				{
					// 切换安装状态
					Context.CurState = new InstallState(downloadFilePath, Context);
				}
				else
				{
					Context.CurState = new FirstCheckState(Context);
					Context.CurState.Start(Context);
				}

				//}
				//else
				//{
				//	// 下载文件错误
				//	LogHelper.Error("File Download Error:" + newFilPath);
				//	Context.OnTipsStateArgs(new TipsEventArgs(string.Format("文件下载出错，请重新尝试")));
				//	DeleteFile(newFilPath);
				//
				//	Context.StateType = StateType.NeedDownload;
				//	Context.CurState = new DownloadState(Context);
				//}
			}
			else
			{
				LogHelper.Error("Download Error Done Message :" + errorMsg);
				Context.OnTipsStateArgs(new TipsEventArgs(string.Format("网络连接失败")));
				// 重新切换下载状态
				Context.StateType = StateType.NeedDownload;
				Context.CurState = new DownloadState(Context);
			}
		}
		public bool CheckFileComplete(long size, string path)
		{
			bool result = false;
			if (File.Exists(path))
			{
				FileInfo fileInfo = new FileInfo(path);
				if (fileInfo.Length == size)
				{
					result = true;
				}
				else
				{
					LogHelper.Error("File Size Compare Error fileSize:" + fileInfo.Length + "url Size:" + size);
				}
			}
			return result;
		}
		public bool CheckFileMd5(string md5, string path)
		{
			return Common.CaulculateFileMd5(path) == md5;
		}
		private void DeleteFile(string path)
		{
			if (File.Exists(path))
			{
				File.Delete(path);
			}

		}
		public override void Pause(StateContext context)
		{
			Context.StateType = StateType.DownloadingStop;
			buttonContent = "继续下载";
			buttonEnabla = true;
			Context.OnButtonStateArgs(new ButtonStateChangeEventArgs(buttonContent, buttonEnabla, true, false, "StartDownloadButton"));
			// old 暂停
			// downloader.SetPause();

			// new 暂停
			if (downloadController != null)
				downloadController.Stop();
		}
		private void DownLoadResource(string url, string path)
		{
			// old 下载
			// downloader = new DownLoadManager(url, path);
			// downloader.DownloadFileCompleted += DownloadFileCompleted;
			// downloader.DownloadStarted += DownloadStarted;
			// downloader.DownloadProgressChanged += DownloadProgressChanged;
			// downloader.DownloadStartedReady += DownloadStartedReady;
			// var limitedSpeed = BaseInfoContentModel.Instance.GetValue<int>("setDownloadSpeed", -1);
			// downloader.SetSpeed(limitedSpeed);
			// await Task.Run(() => downloader.DownloadFileAsync());

			// new 下载
			if (downloadController != null)
			{
				downloadController.Start();
			}
			else
			{
				LogHelper.Info("New DownloadInfo " + Convert.ToBase64String(System.Text.Encoding.Default.GetBytes(url)) + "x");
				var downloadInfo = new DownloadInfo();
				downloadInfo.saveDir = path;
				downloadInfo.downloadUrlList = new List<string> {
				url
			};
				downloadInfo.taskCount = 4;
				downloadController = new DownloadController(downloadInfo);
				downloadController.OnDownload += DownloadManager_OnDownload;
				downloadController.OnStart += DownloadManager_OnStart;
				downloadController.OnStop += DownloadManager_OnStop;
				downloadController.OnFinsh += DownloadManager_OnFinsh;
				downloadController.OnCombineFile += DownloadManager_OnCombine;
				downloadController.OnCombineStartFile += DownloadManager_OnCombineStart;
				downloadController.OnError += DownloadManager_OnError;
				downloadController.Start();
				var limitedSpeed = BaseInfoContentModel.Instance.GetValue<int>("setDownloadSpeed", -1);
				downloadController.SetSpeed(limitedSpeed * 1000);

			}
		}
		private void DownloadManager_OnError(string message)
		{
			if (downloadController == null)
			{
				LogHelper.Info("Download Error downloadController == null");
				return;
			}

			LogHelper.Info("Download Error");

			// 目前下载问题不会影响
			// downloadController.Stop();
			// downloadController = null;
			App.Current.Dispatcher.Invoke((Action)(() =>
			{
				Context.OnTipsStateArgs(new TipsEventArgs("下载错误，出现问题： \n" + message + "\n请重启后重新尝试", () =>
				{
					Common.ClearDownloadFile();
					Environment.Exit(0);
				}, () =>
				{
					Common.ClearDownloadFile();
					Environment.Exit(0);
				}));
			}));

		}
		private void DownloadManager_OnStop()
		{
			if (downloadController == null)
			{
				LogHelper.Info("Download OnStop Error downloadController == null");
				return;
			}

			LogHelper.Info("DownloadOnStop");
			// 暂停下载
			gameDownLoadMessage.Speed = 0;
			SetDownloadShowMessage();
		}
		private void DownloadManager_OnCombine(double progress)
		{
			var info1 = "资源合并中，请稍等...";
			Context.OnProcessStateArgs(new ProcessStateChangeEventArgs(progress, info1, "", ""));
		}
		private void DownloadManager_OnCombineStart()
		{
			buttonContent = "资源合并";
			buttonEnabla = false;
			Context.OnButtonStateArgs(new ButtonStateChangeEventArgs(buttonContent, buttonEnabla, true, false, "StartDownloadButtonGray"));
		}

		private void DownloadManager_OnFinsh()
		{
			// 下载完成
			LogHelper.Info("DownloadOnFinsh");
			Done(Context);
		}

		private void DownloadManager_OnStart(long total)
		{
			if (!CheckDiskIsEnough(total))
			{
				if (downloadController != null)
					downloadController.Stop();
				LogHelper.Error("DownloadStarted Error Disk not enough :" + total);
				App.Current.Dispatcher.Invoke((Action)(() =>
				{
					Action a = () => Context.OnShowInstallPathChangeView(new EventArgs());

					string t = Common.FormatByteNum(total * 2);
					Context.OnTipsStateArgs(new TipsEventArgs("磁盘剩余空间不足，需要更换下载位置\n包大小：" + Common.FormatByteNum(total) + "\n安装所需大小：" + t, a, a));
					DeleteFile(downLoadTempFilePath);
				}));
				Context.StateType = StateType.NeedDownload;
				Context.CurState = new DownloadState(Context);
				return;
			}

			// 开始下载;
			LogHelper.Info("DownloadStartedReady");
			string info1 = "下载游戏资源...";
			string info2 = "下载速度：0.00KB/S";
			string info3 = "";
			gameDownLoadMessage.Total = total;
			Context.OnProcessStateArgs(new ProcessStateChangeEventArgs(downloadPercent, info1, info2, info3));
		}

		private void DownloadManager_OnDownload(long current, double speed)
		{
			if (gameDownLoadMessage.Total == 0)
				return;
			gameDownLoadMessage.Speed = speed;
			gameDownLoadMessage.Precent = (current * 100 / gameDownLoadMessage.Total);
			gameDownLoadMessage.DownloadTotal = current;
			if (double.IsInfinity(speed) || double.IsNaN(speed) || double.IsNegativeInfinity(speed))
				return;
			else
				SetDownloadShowMessage();
		}

		private void SetDownloadShowMessage()
		{
			var total = Common.FormatByteNum(gameDownLoadMessage.Total);
			var downTotal = Common.FormatByteNum(gameDownLoadMessage.DownloadTotal);
			var speed = Common.FormatByteNum(gameDownLoadMessage.Speed);
			string info1 = "下载游戏资源...";
			string info2 = "下载速度：0.00KB/S";
			string info3 = "预计剩余时间：00:00:00";
			do
			{


				if (gameDownLoadMessage.Speed == 0)
				{
					if (gameDownLoadMessage.Precent != 0)
					{
						// 暂停中
						break;
					}
					else
					{
						downloadPercent = 100.0f;
						break;
					}
				}
				else
				{
					var lastTime = (gameDownLoadMessage.Total - gameDownLoadMessage.DownloadTotal) / gameDownLoadMessage.Speed;
					var time = Common.FormatTime((long)Math.Floor(lastTime));
					info1 = string.Format("下载游戏资源{0:F2}%（{1}/{2}）", gameDownLoadMessage.Precent, downTotal, total);
					info2 = string.Format("下载速度：{0}/S", speed);
					info3 = string.Format("预计剩余时间：{0}", time);
					downloadPercent = gameDownLoadMessage.Precent;
					break;
				}

			} while (false);

			Context.OnProcessStateArgs(new ProcessStateChangeEventArgs(downloadPercent, info1, info2, info3));

		}

		private void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
		{
			LogHelper.Info("DownloadComplete");
			if (e.Error != null)
			{
				errorMsg = e.Error.ToString();
			}
			Done(Context);

		}
		private void DownloadStartedReady(object sender, DownloadStartedReadyEventArgs e)
		{
			LogHelper.Info("DownloadStartedReady");
			string info1 = "下载游戏资源...";
			string info2 = "下载速度：0.01KB/S";
			string info3 = "";
			Context.OnProcessStateArgs(new ProcessStateChangeEventArgs(downloadPercent, info1, info2, info3));
			// Context.OnProcessStateArgs(new ProcessStateChangeEventArgs(0, string.Format("第{0}次连接服务器中，请稍等。。。", e.Time), "", ""));
		}
		private void DownloadStarted(object sender, DownloadStartedEventArgs e)
		{
			LogHelper.Info("DownloadStarted TotalBytesToReceive:" + e.TotalBytesToReceive);
			gameDownLoadMessage.Total = e.TotalBytesToReceive;
			// 需要检查文件夹内存是否充足，不充足需要换文件夹
			if (!CheckDiskIsEnough(gameDownLoadMessage.Total))
			{
				if (downloadController != null)
					downloadController.Stop();
				var total = Common.FormatByteNum(gameDownLoadMessage.Total * 2);
				LogHelper.Error("DownloadStarted Error Disk not enough :" + total);
				Action a = () => Context.OnShowInstallPathChangeView(new EventArgs());
				Context.OnTipsStateArgs(new TipsEventArgs(string.Format("磁盘剩余空间不足，需要更换下载位置，所需大小：" + total), a));
				DeleteFile(downLoadTempFilePath);
				Context.StateType = StateType.NeedDownload;
				Context.CurState = new DownloadState(Context);
			}
		}

		private void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
		{
			gameDownLoadMessage.DownloadTotal = e.ReceivedBytesSize;
			gameDownLoadMessage.Speed = e.BytesPerSecondSpeed;
			gameDownLoadMessage.Precent = e.ProgressPercentage;
			SetDownloadShowMessage();
		}




	}
}
